package com.xianggu.spring.aop.config;

import com.xianggu.spring.aop.enums.AdviceType;
import com.xianggu.spring.aop.joinPoint.CglibJoinPoint;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author chengjy
 * @date 2025/3/10
 */
public class AopCglibProxy2Factory implements MethodInterceptor {


    private Object target;

    private List<AdviceParam> adviceParams;
    private List<String> noProxyMethods;


    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Map<AdviceType, List<AdviceParam>> typeListMap = adviceParams.stream().collect(Collectors.groupingBy(AdviceParam::getAdviceType));
        // 跳过toString方法 和 无需代理方法
        if("toString".equals(method.getName()) || (noProxyMethods != null && noProxyMethods.contains(method.getName()) )){
            return methodProxy.invokeSuper(o , objects);
        }
        CglibJoinPoint cglibJoinPoint = new CglibJoinPoint(method, methodProxy, objects, o);
        try{
            // 前置增强
            if (typeListMap.containsKey(AdviceType.BEFORE)) {
                for (AdviceParam adviceParam : typeListMap.get(AdviceType.BEFORE)) {
                    adviceParam.getMethod().invoke(adviceParam.getObject(), cglibJoinPoint);
                }
            }

            Object result = null;
            // 环绕增强
            if (typeListMap.containsKey(AdviceType.AROUND)) {
                for (AdviceParam adviceParam : typeListMap.get(AdviceType.AROUND)) {
                    result = adviceParam.getMethod().invoke(adviceParam.getObject(), cglibJoinPoint);
                }
            }else {
                // 执行原始方法
                result = cglibJoinPoint.proceed();
            }


            // 后置增强
            if (typeListMap.containsKey(AdviceType.AFTER)) {
                for (AdviceParam adviceParam : typeListMap.get(AdviceType.AFTER)) {
                    adviceParam.getMethod().invoke(adviceParam.getObject(), cglibJoinPoint);
                }
            }
            return result;
        }catch (RuntimeException e){
            // 异常增强
            if (typeListMap.containsKey(AdviceType.AFTER_THROWING)) {
                for (AdviceParam adviceParam : typeListMap.get(AdviceType.AFTER_THROWING)) {
                    adviceParam.getMethod().invoke(adviceParam.getObject(), method, e);
                }
            }
            throw e;
        }finally {
            // 最终增强
            if (typeListMap.containsKey(AdviceType.AFTER_RETURNING)) {
                for (AdviceParam adviceParam : typeListMap.get(AdviceType.AFTER_RETURNING)) {
                    adviceParam.getMethod().invoke(adviceParam.getObject(), cglibJoinPoint);
                }
            }
        }
    }

    public AopCglibProxy2Factory(Object target, List<AdviceParam> adviceParams, List<String> noProxyMethods) {
        this.target = target;
        this.adviceParams = adviceParams;
        this.noProxyMethods = noProxyMethods;
    }
}
